1 module hip.api.data.audio;
2 
3 import hip.api.audio;
4 public import hip.api.audio.audioclip;
5 
6 enum HipAudioEncoding
7 {
8     WAV,
9     MP3,
10     OGG,
11     MIDI, //Probably won't support
12     FLAC,
13     MOD,
14     XM
15 }
16 
17 HipAudioEncoding getEncodingFromName(string name)
18 {
19     int i = cast(int)name.length-1;
20     while(i >= 0 && name[i] != '.'){i--;} if(name[i] == '.') i++;
21     switch(name[i..$])
22     {
23         case "wav":return HipAudioEncoding.WAV;
24         case "ogg":return HipAudioEncoding.OGG;
25         case "mp3":return HipAudioEncoding.MP3;
26         case "flac":return HipAudioEncoding.FLAC;
27         case "mod": return HipAudioEncoding.MOD;
28         case "xm": return HipAudioEncoding.XM;
29         case "mid":
30         case "midi":return HipAudioEncoding.MIDI;
31         default: assert(false, "Encoding from file '"~name~", is not supported.");
32     }
33 }
34 
35 interface IHipAudioDecoder
36 {
37     bool decode(in ubyte[] data, HipAudioEncoding encoding, HipAudioType type, 
38     void delegate(in ubyte[] data) onSuccess, void delegate() onFailure);
39 
40     bool resample(in ubyte[] data, HipAudioType type, uint outputSampleRate, uint outputChannels,
41     void delegate(in ubyte[] data) onSuccess, void delegate() onFailure);
42 
43     ///Channel conversion is a very simple implementation, so, I won't use delegates for it.
44     bool channelConversion(in ubyte[] data, ubyte from, ubyte to);
45     /**
46     *   Receives the raw data. Deals with the data based on clip hint.
47     */
48     final bool loadData(in ubyte[] data, HipAudioEncoding encoding, HipAudioType type, HipAudioClipHint hint,
49     void delegate(in ubyte[] data) onSuccess, void delegate() onFailure)
50     {
51         if(data.length == 0)
52         {
53             onFailure();
54             return false;
55         }
56 
57         if(hint.needsDecode)
58         {
59             decode(data, encoding, type, (in ubyte[] decodedData)
60             {
61                 if(hint.needsResample && getSamplerate != hint.outputSamplerate)
62                 {
63                     resample(decodedData, type, hint.outputSamplerate, hint.outputChannels, (in ubyte[] resampledData)
64                     {
65                         if(hint.needsChannelConversion && getClipChannels != hint.outputChannels &&
66                         !channelConversion(resampledData, getClipChannels, cast(ubyte)hint.outputChannels))
67                         {
68                             onFailure(); 
69                             return;
70                         }
71                         onSuccess(getClipData);
72                     }, onFailure);
73                 }
74                 else
75                     onSuccess(decodedData);
76             }, onFailure);
77         }
78         else if(hint.needsResample && getSamplerate != hint.outputSamplerate)
79         {
80             resample(getClipData, type, hint.outputSamplerate, hint.outputChannels, (in ubyte[] resampledData)
81             {
82                 if(hint.needsChannelConversion && getClipChannels != hint.outputChannels &&
83                 !channelConversion(resampledData, getClipChannels, cast(ubyte)hint.outputChannels))
84                     onFailure(); 
85                 onSuccess(getClipData);
86             }, onFailure);
87         }
88         else if(hint.needsChannelConversion && getClipChannels != hint.outputChannels)
89         {
90             channelConversion(getClipData, getClipChannels, cast(ubyte)hint.outputChannels);
91             onSuccess(getClipData);
92         }
93 
94         return true;
95     }
96     ///Used for streaming.
97     uint startDecoding(in ubyte[] data, ubyte[] outputDecodedData, uint chunkSize, HipAudioEncoding encoding)
98     in (chunkSize > 0 , "Chunk size must be greater than 0");
99     uint updateDecoding(ubyte[] outputDecodedData);
100     AudioConfig getAudioConfig();
101     ubyte[] getClipData();
102     ubyte getClipChannels();
103     size_t getClipSize();
104     ///Don't apply to streamed audio. Gets the duration in seconds
105     float getDuration();
106 
107     void dispose();
108     uint getSamplerate();
109 
110 }
111 
112 enum AudioFormat : ushort
113 {
114     signed8,
115     signed16Little,
116     signed16Big,
117     signed32Little,
118     signed32Big,
119     unsigned8,
120     unsigned16Little,
121     unsigned16Big,
122     float32Little,
123     float32Big,
124     _default = signed16Little
125 }
126 
127 enum audioConfigDefaultBufferSize = 4096;
128 
129 
130 struct AudioConfig
131 {
132     int sampleRate;
133     AudioFormat format;
134     uint channels;
135     int bufferSize;
136 
137     static enum defaultBufferSize = audioConfigDefaultBufferSize;
138 
139 
140 
141     /**
142     *   Returns a default audio configuration for 2D
143     */
144     static AudioConfig musicConfig()
145     {
146         return AudioConfig(44_100, AudioFormat.float32Little, 2, audioConfigDefaultBufferSize);
147     }
148     static AudioConfig androidConfig()
149     {
150         return AudioConfig(22_050, AudioFormat.float32Little, 1U, 2048);
151     }
152     static AudioConfig lightweightConfig()
153     {
154         return AudioConfig(22_050, AudioFormat._default, 1U, 2048);
155     }
156 
157     uint getBitDepth()
158     {
159         switch(format) with(AudioFormat)
160         {
161             case signed8:
162             case unsigned8:
163                 return 8;
164             case signed16Big:
165             case signed16Little:
166             case unsigned16Big:
167             case unsigned16Little:
168                 return 16;
169             case signed32Big:
170             case signed32Little:
171             case float32Big:
172             case float32Little:
173                 return 32;
174             default:return 0;
175         }
176     }
177 }